package org.example.auth.config.web;

import lombok.RequiredArgsConstructor;
import org.example.common.core.constants.CacheConstants;
import org.example.common.core.domain.R;
import org.example.common.core.exception.oauth.MyInvalidClientException;
import org.example.common.redis.utils.RedisUtils;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint;
import org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.security.oauth2.provider.endpoint.TokenKeyEndpoint;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;

import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.Map;

/**
 *重写oauth实现接口
 */
@RestController
@RequiredArgsConstructor
public class OAuthController {

    private final TokenEndpoint tokenEndpoint;
    private final CheckTokenEndpoint checkTokenEndpoint;
    private final AuthorizationEndpoint authorizationEndpoint;
    private final TokenKeyEndpoint tokenKeyEndpoint;

    @GetMapping("/oauth/token")
    public Object getAccessToken(Principal principal, @RequestParam
            Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        try{
            OAuth2AccessToken auth2AccessToken =  tokenEndpoint.getAccessToken(principal,parameters).getBody();
            return R.ok(auth2AccessToken);
        }catch (InvalidClientException exception){
            throw new MyInvalidClientException(exception.getMessage());
        }
    }

    @PostMapping("/oauth/token")
    public Object postAccessToken(Principal principal, @RequestParam
            Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        try{
            OAuth2AccessToken auth2AccessToken =tokenEndpoint.postAccessToken(principal,parameters).getBody();
            return  R.ok(auth2AccessToken);
        }catch (InvalidClientException exception){
            throw new MyInvalidClientException(exception.getMessage());
        }
    }

    @GetMapping("/oauth/check_token")
    public Object checkToken(@RequestParam("token") String value) {
        Map checkToken =checkTokenEndpoint.checkToken(value);
        return checkToken;
    }


    @RequestMapping(value = "/oauth/authorize")
    public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters,
                                  SessionStatus sessionStatus, Principal principal) {
        return authorizationEndpoint.authorize(model,parameters,sessionStatus,principal);
    }

    static final String AUTHORIZATION_REQUEST_ATTR_NAME = "authorizationRequest";
    static final String ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME = "org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST";

    @RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)
    public View approveOrDeny(@RequestParam Map<String, String> approvalParameters, Map<String, Object> model,
                              SessionStatus sessionStatus, Principal principal) {
        String clientId= approvalParameters.get("clientId");
        String key=String.format("%s:%s",CacheConstants.AUTHORIZATION_MODEL,clientId);
        if(RedisUtils.isExistsObject(key)){
            AuthorizationRequest authorizationRequest= RedisUtils.getCacheMapValue(key,AUTHORIZATION_REQUEST_ATTR_NAME);
            Map<String, Object> originalAuthorizationRequest = RedisUtils.getCacheMapValue(key,ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME);
            model.put(AUTHORIZATION_REQUEST_ATTR_NAME,authorizationRequest);
            model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME,originalAuthorizationRequest);
            RedisUtils.deleteObject(key);
        }
        return authorizationEndpoint.approveOrDeny(approvalParameters,model,sessionStatus,principal);
    }


    @RequestMapping(value = "/oauth/token_key", method = RequestMethod.GET)
    @ResponseBody
    public Map<String, String> getKey(Principal principal) {
        return  tokenKeyEndpoint.getKey(principal);
    }

}
